home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  36.7 KB  |  1,426 lines

  1. /*            File Access                HTFile.c
  2. **            ===========
  3. **
  4. **    This is unix-specific code in general, with some VMS bits.
  5. **    These are routines for file access used by browsers.
  6. **
  7. ** History:
  8. **       Feb 91    Written Tim Berners-Lee CERN/CN
  9. **       Apr 91    vms-vms access included using DECnet syntax
  10. **    26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
  11. **            Fixed access bug for relative names on VMS.
  12. **       Sep 93 (MD)  Access to VMS files allows sharing.
  13. **    15 Nov 93 (MD)    Moved HTVMSname to HTVMSUTILS.C
  14. **      27 Dec 93 (FM)  FTP now works with VMS hosts.
  15. **            FTP path must be Unix-style and cannot include
  16. **             the device or top directory.
  17. */
  18.  
  19. #ifndef VMS
  20. /* #define LONG_LIST  /* Define this for long style unix listings (ls -l) */
  21. /* #define NO_PARENT_DIR_REFERENCE  /* Define this for no parent links */
  22. #endif /* !VMS */
  23.  
  24. #include "HTUtils.h"
  25. #include "HTFile.h"        /* Implemented here */
  26. #ifdef VMS
  27. #include <stat.h>
  28. #endif /* VMS */
  29.  
  30. #ifndef VMS
  31. #ifdef LONG_LIST
  32. #include <pwd.h>
  33. #include <grp.h>
  34. #endif /* LONG_LIST */
  35. #endif /* !VMS */
  36.   
  37. #define INFINITY 512        /* file name length @@ FIXME */
  38. #define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
  39.  
  40. #define HT_EM_SPACE ((char)2)
  41.  
  42. #define FREE(x) if (x) {free(x); x = NULL;}
  43.  
  44. #ifdef VMS
  45. #include "HTVMSUtils.h"
  46. #endif /* VMS */
  47.  
  48. #include "HTParse.h"
  49. #include "tcp.h"
  50. #include "HTTCP.h"
  51. #ifndef DECNET
  52. #include "HTFTP.h"
  53. #endif /* !DECNET */
  54. #include "HTAnchor.h"
  55. #include "HTAtom.h"
  56. #include "HTWriter.h"
  57. #include "HTFWriter.h"
  58. #include "HTInit.h"
  59. #include "HTBTree.h"
  60.  
  61. #include "LYexit.h"
  62. #include "LYLeaks.h"
  63.  
  64. typedef struct _HTSuffix {
  65.     char *        suffix;
  66.     HTAtom *    rep;
  67.     HTAtom *    encoding;
  68.     float        quality;
  69. } HTSuffix;
  70.  
  71. #ifndef NGROUPS
  72. #ifdef NGROUPS_MAX
  73. #define NGROUPS NGROUPS_MAX
  74. #else
  75. #define NGROUPS 32
  76. #endif /* NGROUPS_MAX */
  77. #endif /* NGROUPS */
  78.  
  79.  
  80. #ifdef USE_DIRENT        /* Set this for Sys V systems */
  81. #define STRUCT_DIRENT struct dirent
  82. #else
  83. #define STRUCT_DIRENT struct direct
  84. #endif /* USE_DIRENT */
  85.  
  86. #include "HTML.h"        /* For directory object building */
  87.  
  88. #define PUTC(c) (*target->isa->put_character)(target, c)
  89. #define PUTS(s) (*target->isa->put_string)(target, s)
  90. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  91. #define END(e) (*target->isa->end_element)(target, e)
  92. #define FREE_TARGET (*target->isa->_free)(target)
  93. struct _HTStructured {
  94.     CONST HTStructuredClass *    isa;
  95.     /* ... */
  96. };
  97.  
  98.  
  99. /*                   Controlling globals
  100. **
  101. */
  102.  
  103. PUBLIC int HTDirAccess = HT_DIR_OK;
  104.  
  105. #ifdef DIRED_SUPPORT
  106. PUBLIC int HTDirReadme = HT_DIR_README_NONE;
  107. #define FILES_FIRST 1
  108. #define MIXED_STYLE 2
  109. extern BOOLEAN lynx_edit_mode;
  110. extern BOOLEAN dir_list_style;
  111. #else
  112. PUBLIC int HTDirReadme = HT_DIR_README_TOP;
  113. #endif /* DIRED_SUPPORT */
  114.  
  115. PRIVATE char *HTMountRoot = "/Net/";        /* Where to find mounts */
  116. #ifdef VMS
  117. PRIVATE char *HTCacheRoot = "/WWW$SCRATCH";   /* Where to cache things */
  118. #else
  119. PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
  120. #endif /* VMS */
  121.  
  122. /* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
  123.  
  124.  
  125. /*    Suffix registration
  126. */
  127.  
  128. PRIVATE HTList * HTSuffixes = 0;
  129. PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
  130. PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
  131.  
  132. /*
  133.  *    To free up the suffixes at program exit.
  134.  */
  135. static void free_suffixes NOPARAMS;
  136.  
  137. /*    Define the representation associated with a file suffix
  138. **    -------------------------------------------------------
  139. **
  140. **    Calling this with suffix set to "*" will set the default
  141. **    representation.
  142. **    Calling this with suffix set to "*.*" will set the default
  143. **    representation for unknown suffix files which contain a ".".
  144. **
  145. **    If filename suffix is already defined its previous
  146. **    definition is overridden.
  147. */
  148. PUBLIC void HTSetSuffix ARGS4(
  149.     CONST char *,    suffix,
  150.     CONST char *,    representation,
  151.     CONST char *,    encoding,
  152.     float,        value)
  153. {
  154.     
  155.     HTSuffix * suff;
  156.  
  157.     if (strcmp(suffix, "*")==0) suff = &no_suffix;
  158.     else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
  159.     else {
  160.     HTList *cur = HTSuffixes;
  161.  
  162.     while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) {
  163.         if (suff->suffix && 0==strcmp(suff->suffix, suffix))
  164.         break;
  165.     }
  166.     if (!suff) { /* Not found -- create a new node */
  167.         suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
  168.         if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
  169.  
  170.         /*
  171.              *    Memory leak fixed.
  172.          *    05-28-94 Lynx 2-3-1 Garrett Arch Blythe
  173.          */    
  174.         if (!HTSuffixes)    {
  175.         HTSuffixes = HTList_new();
  176.         atexit(free_suffixes);
  177.         }
  178.  
  179.         HTList_addObject(HTSuffixes, suff);
  180.     
  181.         StrAllocCopy(suff->suffix, suffix);
  182.     }
  183.     }
  184.  
  185.     suff->rep = HTAtom_for(representation);
  186.    
  187.     /*
  188.      *    Memory leak fixed.
  189.      *    05-28-94 Lynx 2-3-1 Garrett Arch Blythe
  190.      *    Invariant code removed.
  191.      */
  192.     suff->encoding = HTAtom_for(encoding);
  193.     
  194.     suff->quality = value;
  195. }
  196.  
  197. static void free_suffixes NOARGS    {
  198. /*
  199.  *    Purpose:    Free all added suffixes.
  200.  *    Arguments:    void
  201.  *    Return Value:    void
  202.  *    Remarks/Portability/Dependencies/Restrictions:
  203.  *        To be used at program exit.
  204.  *    Revision History:
  205.  *        05-28-94    created Lynx 2-3-1 Garrett Arch Blythe
  206.  */
  207.  
  208.     /*
  209.      *    Loop through all suffixes.
  210.      */
  211.     while(!HTList_isEmpty(HTSuffixes))    {
  212.         /*
  213.          *    Free off each item and its members if need be.
  214.          */
  215.         free(HTList_removeLastObject(HTSuffixes));
  216.     }
  217.     /*
  218.      *    Free off the list itself.
  219.       */
  220.     HTList_delete(HTSuffixes);
  221. }
  222.  
  223.  
  224.  
  225.  
  226.  
  227. /*    Send README file
  228. **
  229. **  If a README file exists, then it is inserted into the document here.
  230. */
  231.  
  232. #ifdef GOT_READ_DIR
  233. PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
  234.     FILE * fp;
  235.     char * readme_file_name = 
  236.     malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
  237.     strcpy(readme_file_name, localname);
  238.     strcat(readme_file_name, "/");
  239.     strcat(readme_file_name, HT_DIR_README_FILE);
  240.     
  241.     fp = fopen(readme_file_name,  "r");
  242.     
  243.     if (fp) {
  244.     HTStructuredClass targetClass;
  245.     
  246.     targetClass =  *target->isa;    /* (Can't init agregate in K&R) */
  247.     START(HTML_PRE);
  248.     for(;;){
  249.         char c = fgetc(fp);
  250.         if (c == (char)EOF) break;
  251.         switch (c) {
  252.             case '&':
  253.         case '<':
  254.         case '>':
  255.             PUTC('&');
  256.             PUTC('#');
  257.             PUTC((char)(c / 10));
  258.             PUTC((char) (c % 10));
  259.             PUTC(';');
  260.             break;
  261. /*            case '\n':
  262.             PUTC('\r');    
  263. Bug removed thanks to joe@athena.mit.edu */            
  264.         default:
  265.             PUTC(c);
  266.         }
  267.     }
  268.     END(HTML_PRE);
  269.     fclose(fp);
  270.     } 
  271. }
  272. #endif /* GOT_READ_DIR */
  273.  
  274.  
  275. /*    Make the cache file name for a W3 document
  276. **    ------------------------------------------
  277. **    Make up a suitable name for saving the node in
  278. **
  279. **    E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
  280. **        /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
  281. **
  282. ** On exit,
  283. **    returns    a malloc'ed string which must be freed by the caller.
  284. */
  285. PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
  286. {
  287.     char * access = HTParse(name, "", PARSE_ACCESS);
  288.     char * host = HTParse(name, "", PARSE_HOST);
  289.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  290.     
  291.     char * result;
  292.     result = (char *)malloc(
  293.         strlen(HTCacheRoot)+strlen(access)
  294.         +strlen(host)+strlen(path)+6+1);
  295.     if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
  296.     sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
  297.     FREE(path);
  298.     FREE(access);
  299.     FREE(host);
  300.     return result;
  301. }
  302.  
  303.  
  304. /*    Open a file for write, creating the path
  305. **    ----------------------------------------
  306. */
  307. #ifdef NOT_IMPLEMENTED
  308. PRIVATE int HTCreatePath ARGS1(CONST char *,path)
  309. {
  310.     return -1;
  311. }
  312. #endif /* NOT_IMPLEMENTED */
  313.  
  314. /*    Convert filenames between local and WWW formats
  315. **    -----------------------------------------------
  316. **    Make up a suitable name for saving the node in
  317. **
  318. **    E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
  319. **        $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
  320. **
  321. ** On exit,
  322. **    returns    a malloc'ed string which must be freed by the caller.
  323. */
  324. PUBLIC char * HTLocalName ARGS1(CONST char *,name)
  325. {
  326.     char * access = HTParse(name, "", PARSE_ACCESS);
  327.     char * host = HTParse(name, "", PARSE_HOST);
  328.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  329.     
  330.     HTUnEscape(path);    /* Interpret % signs */
  331.     
  332.     if (0==strcmp(access, "file")) { /* local file */
  333.         FREE(access);    
  334.     if ((0==strcasecomp(host, HTHostName())) ||
  335.         (0==strcasecomp(host, "localhost")) || !*host) {
  336.         FREE(host);
  337.         if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
  338.         return(path);
  339.     } else {
  340.         char * result = (char *)malloc(
  341.                     strlen("/Net/")+strlen(host)+strlen(path)+1);
  342.               if (result == NULL) outofmem(__FILE__, "HTLocalName");
  343.         sprintf(result, "%s%s%s", "/Net/", host, path);
  344.         FREE(host);
  345.         FREE(path);
  346.         if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
  347.         return result;
  348.     }
  349.     } else {  /* other access */
  350.     char * result;
  351. #ifdef VMS
  352.         char * home =  getenv("HOME");
  353.     if (!home) 
  354.         home = HTCacheRoot; 
  355.     else
  356.            home = HTVMS_wwwName(home);
  357. #else
  358.         CONST char * home =  (CONST char*)getenv("HOME");
  359.     if (!home) home = "/tmp"; 
  360. #endif /* VMS */
  361.     result = (char *)malloc(
  362.         strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
  363.       if (result == NULL) outofmem(__FILE__, "HTLocalName");
  364.     sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
  365.     FREE(path);
  366.     FREE(access);
  367.     FREE(host);
  368.     return result;
  369.     }
  370. }
  371.  
  372.  
  373. /*    Make a WWW name from a full local path name
  374. **
  375. ** Bugs:
  376. **    At present, only the names of two network root nodes are hand-coded
  377. **    in and valid for the NeXT only. This should be configurable in
  378. **    the general case.
  379. */
  380.  
  381. PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
  382. {
  383.     char * result;
  384. #ifdef NeXT
  385.     if (0==strncmp("/private/Net/", name, 13)) {
  386.     result = (char *)malloc(7+strlen(name+13)+1);
  387.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  388.     sprintf(result, "file://%s", name+13);
  389.     } else
  390. #endif /* NeXT */
  391.     if (0==strncmp(HTMountRoot, name, 5)) {
  392.     result = (char *)malloc(7+strlen(name+5)+1);
  393.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  394.     sprintf(result, "file://%s", name+5);
  395.     } else {
  396.         result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
  397.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  398.     sprintf(result, "file://%s%s", HTHostName(), name);
  399.     }
  400.     if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
  401.     return result;
  402. }
  403.  
  404.  
  405. /*    Determine a suitable suffix, given the representation
  406. **    -----------------------------------------------------
  407. **
  408. ** On entry,
  409. **    rep    is the atomized MIME style representation
  410. **
  411. ** On exit,
  412. **    returns    a pointer to a suitable suffix string if one has been
  413. **        found, else "".
  414. */
  415. PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
  416. {
  417.     HTSuffix * suff;
  418.     int n;
  419.     int i;
  420.  
  421. #define NO_INIT  /* dont init anymore since I do it in Lynx at startup */
  422. #ifndef NO_INIT    
  423.     if (!HTSuffixes) HTFileInit();
  424. #endif /* !NO_INIT */
  425.     n = HTList_count(HTSuffixes);
  426.     for(i=0; i<n; i++) {
  427.     suff = HTList_objectAt(HTSuffixes, i);
  428.     if (suff->rep == rep) {
  429.         return suff->suffix;        /* OK -- found */
  430.     }
  431.     }
  432.     return "";        /* Dunno */
  433. }
  434.  
  435.  
  436. /*    Determine file format from file name
  437. **    ------------------------------------
  438. **
  439. **    This version will return the representation and also set
  440. **    a variable for the encoding.
  441. **
  442. **    It will handle for example  x.txt, x.txt,Z, x.Z
  443. */
  444.  
  445. PUBLIC HTFormat HTFileFormat ARGS2 (
  446.             CONST char *,    filename,
  447.             HTAtom **,    pencoding)
  448.  
  449. {
  450.     HTSuffix * suff;
  451.     int n;
  452.     int i;
  453.     int lf;
  454. #ifdef VMS
  455.     char *semicolon;
  456. #endif /* VMS */
  457.     extern char LYforce_HTML_mode;
  458.  
  459.     if(LYforce_HTML_mode) {
  460.         LYforce_HTML_mode = FALSE;
  461.         return WWW_HTML;
  462.     }
  463.  
  464. #ifdef VMS
  465.     /*
  466.      * Trim at semicolon if a version number was
  467.      * included, so it doesn't interfere with the
  468.      * code for getting the MIME type. - FM
  469.      */
  470.     if ((semicolon=strchr(filename, ';')) != NULL)
  471.         *semicolon = '\0';
  472. #endif /* VMS */
  473.  
  474. #ifndef NO_INIT    
  475.     if (!HTSuffixes) HTFileInit();
  476. #endif /* !NO_INIT */
  477.     *pencoding = NULL;
  478.     lf  = strlen(filename);
  479.     n = HTList_count(HTSuffixes);
  480.     for(i=0; i<n; i++) {
  481.         int ls;
  482.     suff = HTList_objectAt(HTSuffixes, i);
  483.     ls = strlen(suff->suffix);
  484.     if ((ls <= lf) && 0==strcasecomp(suff->suffix, filename + lf - ls)) {
  485.         int j;
  486.         *pencoding = suff->encoding;
  487.         if (suff->rep) {
  488. #ifdef VMS
  489.         if (semicolon != NULL)
  490.             *semicolon = ';';
  491. #endif /* VMS */
  492.             return suff->rep;        /* OK -- found */
  493.         }
  494.         for(j=0; j<n; j++) {  /* Got encoding, need representation */
  495.         int ls2;
  496.         suff = HTList_objectAt(HTSuffixes, j);
  497.         ls2 = strlen(suff->suffix);
  498.         if ((ls <= lf) && 0==strncasecomp(
  499.             suff->suffix, filename + lf - ls -ls2, ls2)) {
  500.             if (suff->rep) {
  501. #ifdef VMS
  502.             if (semicolon != NULL)
  503.                 *semicolon = ';';
  504. #endif /* VMS */
  505.                 return suff->rep;
  506.             }
  507.         }
  508.         }
  509.         
  510.     }
  511.     }
  512.     
  513.     /* defaults tree */
  514.     
  515.     suff = strchr(filename, '.') ?     /* Unknown suffix */
  516.          ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
  517.      : &no_suffix;
  518.      
  519.     /* set default encoding unless found with suffix already */
  520.     if (!*pencoding) *pencoding = suff->encoding ? suff->encoding
  521.                     : HTAtom_for("binary");
  522. #ifdef VMS
  523.     if (semicolon != NULL)
  524.         *semicolon = ';';
  525. #endif /* VMS */
  526.     return suff->rep ? suff->rep : WWW_BINARY;
  527. }
  528.  
  529.  
  530. /*    Determine value from file name
  531. **    ------------------------------
  532. **
  533. */
  534.  
  535. PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
  536.  
  537. {
  538.     HTSuffix * suff;
  539.     int n;
  540.     int i;
  541.     int lf = strlen(filename);
  542.  
  543. #ifndef NO_INIT    
  544.     if (!HTSuffixes) HTFileInit();
  545. #endif /* !NO_INIT */
  546.     n = HTList_count(HTSuffixes);
  547.     for(i=0; i<n; i++) {
  548.         int ls;
  549.     suff = HTList_objectAt(HTSuffixes, i);
  550.     ls = strlen(suff->suffix);
  551.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  552.         if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
  553.                    filename, suff->quality);
  554.         return suff->quality;        /* OK -- found */
  555.     }
  556.     }
  557.     return 0.3;        /* Dunno! */
  558. }
  559.  
  560.  
  561. /*    Determine write access to a file
  562. **    --------------------------------
  563. **
  564. ** On exit,
  565. **    return value    YES if file can be accessed and can be written to.
  566. **
  567. ** Bugs:
  568. **    1.    No code for non-unix systems.
  569. **    2.    Isn't there a quicker way?
  570. */
  571.  
  572. #ifdef VMS
  573. #define NO_GROUPS
  574. #endif /* VMS */
  575. #ifdef NO_UNIX_IO
  576. #define NO_GROUPS
  577. #endif /* NO_UNIX_IO */
  578. #ifdef PCNFS
  579. #define NO_GROUPS
  580. #endif /* PCNFS */
  581.  
  582. PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
  583. {
  584. #ifdef NO_GROUPS
  585.     return NO;        /* Safe answer till we find the correct algorithm */
  586. #else
  587.     int     groups[NGROUPS];    
  588.     uid_t    myUid;
  589.     int        ngroups;            /* The number of groups  */
  590.     struct stat    fileStatus;
  591.     int        i;
  592.         
  593.     if (stat(filename, &fileStatus))        /* Get details of filename */
  594.         return NO;                /* Can't even access file! */
  595.  
  596.     ngroups = getgroups(NGROUPS, groups);    /* Groups to which I belong  */
  597.     myUid = geteuid();                /* Get my user identifier */
  598.  
  599.     if (TRACE) {
  600.         int i;
  601.     fprintf(stderr, 
  602.         "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
  603.             (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
  604.         fileStatus.st_gid,
  605.         myUid, ngroups);
  606.     for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
  607.     fprintf(stderr, ")\n");
  608.     }
  609.     
  610.     if (fileStatus.st_mode & 0002)        /* I can write anyway? */
  611.         return YES;
  612.     
  613.     if ((fileStatus.st_mode & 0200)        /* I can write my own file? */
  614.      && (fileStatus.st_uid == myUid))
  615.         return YES;
  616.  
  617.     if (fileStatus.st_mode & 0020)        /* Group I am in can write? */
  618.     {
  619.        for (i=0; i<ngroups; i++) {
  620.             if (groups[i] == fileStatus.st_gid)
  621.             return YES;
  622.     }
  623.     }
  624.     if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
  625.     return NO;                    /* If no excuse, can't do */
  626. #endif /* NO_GROUPS */
  627. }
  628.  
  629.  
  630. /*    Make a save stream
  631. **    ------------------
  632. **
  633. **    The stream must be used for writing back the file.
  634. **    @@@ no backup done
  635. */
  636. PUBLIC HTStream * HTFileSaveStream ARGS1(HTParentAnchor *, anchor)
  637. {
  638.  
  639.     CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
  640.     char *  localname = HTLocalName(addr);
  641.     
  642.     FILE* fp = fopen(localname, "w");
  643.     if (!fp) return NULL;
  644.     
  645.     return HTFWriter_new(fp);
  646.     
  647. }
  648.  
  649. /*      Output one directory entry
  650. **
  651. */
  652. PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
  653.          CONST char * , tail,
  654.          CONST char *,  entry)
  655. {
  656.     char * relative;
  657.     char * escaped = HTEscape(entry, URL_XPALPHAS);
  658.  
  659.  
  660.     /* handle extra slash at end of path */
  661.     if(*tail == '\0') {
  662.         HTStartAnchor(target, NULL, escaped);
  663.     } else {
  664.         /* If empty tail, gives absolute ref below */
  665.         relative = (char*) malloc(strlen(tail) + strlen(escaped)+2);
  666.         if (relative == NULL) outofmem(__FILE__, "DirRead");
  667.         sprintf(relative, "%s/%s", tail, escaped);
  668.         HTStartAnchor(target, NULL, relative);
  669.         FREE(relative);
  670.     }
  671.     FREE(escaped);
  672. }
  673.  
  674. /*      Output parent directory entry
  675. **
  676. **    This gives the TITLE and H1 header, and also a link
  677. **    to the parent directory if appropriate.
  678. */
  679. PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
  680.          HTAnchor * , anchor)
  681.  
  682. {
  683.     char * logical = HTAnchor_address(anchor);
  684.     char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
  685.     char * current;
  686.  
  687.     current = strrchr(path, '/');    /* last part or "" */
  688.  
  689.     {
  690.       char * printable = NULL;
  691.  
  692. #ifdef DIRED_SUPPORT
  693.       StrAllocCopy(printable, path);
  694. #else 
  695.       StrAllocCopy(printable, (current + 1));
  696. #endif /* DIRED_SUPPORT */
  697.  
  698.       START(HTML_HEAD);
  699.       PUTS("\n");
  700.       HTUnEscape(printable);
  701.       START(HTML_TITLE);
  702.       PUTS(*printable ? printable : "Welcome ");
  703.       PUTS(" directory");
  704.       END(HTML_TITLE);
  705.       PUTS("\n");
  706.       END(HTML_HEAD);
  707.       PUTS("\n");
  708.  
  709. #ifdef DIRED_SUPPORT
  710.       START(HTML_H2);
  711.       PUTS(*printable ? "Current directory is " : "");
  712.       PUTS(*printable ? printable : "Welcome");
  713.       END(HTML_H2);
  714.       PUTS("\n");
  715. #else
  716.       START(HTML_H1);
  717.       PUTS(*printable ? printable : "Welcome");
  718.       END(HTML_H1);
  719.       PUTS("\n");
  720. #endif /* DIRED_SUPPORT */
  721.       FREE(printable);
  722.     }
  723.  
  724. #ifndef NO_PARENT_DIR_REFERENCE
  725.     /*  Make link back to parent directory
  726.      */
  727.  
  728.     if (current && current[1]) {   /* was a slash AND something else too */
  729.         char * parent;
  730.     char * relative;
  731.     *current++ = 0;
  732.         parent = strrchr(path, '/');  /* penultimate slash */
  733.  
  734.     relative = (char*) malloc(strlen(current) + 4);
  735.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  736.     sprintf(relative, "%s/..", current);
  737. #ifndef VMS
  738.     {
  739.         /*
  740.          *  On Unix, if it's not ftp and the directory cannot
  741.          *  be read, don't put out a link.
  742.          *
  743.          *  On VMS, this problem is dealt with internally by
  744.          *  HTVMSBrowseDir().
  745.          */
  746.         extern BOOLEAN LYisLocalFile PARAMS((char *logical));
  747.         DIR  * dp=NULL;
  748.  
  749.         if (LYisLocalFile(logical)) {
  750.             if ((dp = opendir(relative)) == NULL) {
  751.                 FREE(logical);
  752.                 FREE(relative);
  753.                 FREE(path);
  754.                 return;
  755.         }
  756.         if (dp)
  757.                 closedir(dp);
  758.         }
  759.     }
  760. #endif /* !VMS */
  761.     HTStartAnchor(target, "", relative);
  762.     FREE(relative);
  763.  
  764. #ifdef DIRED_SUPPORT
  765.     if (dir_list_style != MIXED_STYLE)
  766. #endif /* DIRED_SUPPORT */
  767.         PUTS("Up to ");
  768.     if (parent) {
  769. #ifdef DIRED_SUPPORT
  770.        if (dir_list_style == MIXED_STYLE) {
  771.           PUTS("../");
  772.        } else {
  773. #else
  774.        {
  775. #endif /* DIRED_SUPPORT */
  776.           char * printable = NULL;
  777.           StrAllocCopy(printable, parent + 1);
  778.           HTUnEscape(printable);
  779.           PUTS(printable);
  780.           FREE(printable);
  781.        }
  782.     } else {
  783.       PUTS("/");
  784.     }
  785.  
  786.     END(HTML_A);
  787.     }
  788. #endif /* NO_PARENT_DIR_REFERENCE */
  789.  
  790.     FREE(logical);
  791.     FREE(path);
  792. }
  793.         
  794.  
  795.  
  796. /*    Load a document
  797. **    ---------------
  798. **
  799. ** On entry,
  800. **    addr        must point to the fully qualified hypertext reference.
  801. **            This is the physsical address of the file
  802. **
  803. ** On exit,
  804. **    returns        <0        Error has occured.
  805. **            HTLOADED    OK 
  806. **
  807. */
  808. PUBLIC int HTLoadFile ARGS4 (
  809.     CONST char *,        addr,
  810.     HTParentAnchor *,    anchor,
  811.     HTFormat,        format_out,
  812.     HTStream *,        sink
  813. )
  814. {
  815.     char * filename;
  816.     char * access;
  817.     HTFormat format;
  818.     char * nodename=NULL;
  819.     char * newname=NULL; /* Simplified name of file */
  820.     HTAtom * encoding;     /* @@ not used yet */
  821. #ifdef VMS
  822.     struct stat stat_info;
  823. #endif /* VMS */
  824.  
  825.     FREE(nodename);    /* From prev call - Leak fixed AL 6 Feb 1994 */
  826.     
  827. /*    Reduce the filename to a basic form (hopefully unique!)
  828. */
  829.     StrAllocCopy(newname, addr);
  830.     filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
  831.     nodename=HTParse(newname, "", PARSE_HOST);
  832.     
  833.     /* If access is ftp, or file is on another host, invoke ftp now */
  834.     access = HTParse(newname, "", PARSE_ACCESS);
  835.     if(strcmp("ftp", access) == 0 ||
  836.        (strcmp("localhost", nodename) != 0 &&
  837. #ifdef VMS
  838.         strcasecomp(nodename, HTHostName()) != 0))
  839. #else
  840.         strcmp(nodename, HTHostName()) != 0))
  841. #endif /* VMS */
  842.     {
  843.         FREE(newname);
  844.         FREE(access);
  845.     FREE(filename);
  846.         return HTFTPLoad(addr, anchor, format_out, sink);
  847.     } else {
  848.         FREE(newname);
  849.         FREE(access);
  850.     }
  851.  
  852. #ifdef VMS
  853. /*
  854. **    Check to see if the 'filename' is in fact a directory.  If it is
  855. **    create a new hypertext object containing a list of files and 
  856. **    subdirectories contained in the directory.  All of these are links
  857. **      to the directories or files listed.
  858. */
  859.     HTUnEscape(filename);
  860.     if (HTStat(filename, &stat_info) == -1) {
  861.     if (TRACE)
  862.         fprintf(stderr, "HTLoadFile.. Can't stat %s\n", filename);
  863.     } else {
  864.     if (((stat_info.st_mode) & S_IFMT) == S_IFDIR) {
  865.         if (HTDirAccess == HT_DIR_FORBID) {
  866.         FREE(filename);
  867.         return HTLoadError(sink, 403,
  868.         "Directory browsing is not allowed.");
  869.         }
  870.  
  871.         if (HTDirAccess == HT_DIR_SELECTIVE) {
  872.         char * enable_file_name = 
  873.             malloc(strlen(filename)+ 1 +
  874.             strlen(HT_DIR_ENABLE_FILE) + 1);
  875.         strcpy(enable_file_name, filename);
  876.         strcat(enable_file_name, "/");
  877.         strcat(enable_file_name, HT_DIR_ENABLE_FILE);
  878.         if (HTStat(enable_file_name, &stat_info) == -1) {
  879.             FREE(filename);
  880.             return HTLoadError(sink, 403,
  881.             "Selective access is not enabled for this directory");
  882.         }
  883.         }
  884.  
  885.         FREE(filename);
  886.         return HTVMSBrowseDir(addr, anchor, format_out, sink);
  887.     }
  888.     }
  889.  
  890.     format = HTFileFormat(filename, &encoding);
  891.  
  892. /* Assume that the file is in Unix-style syntax if it contains a '/'
  893.    after the leading one @@ */
  894.     {
  895.         FILE * fp;
  896.     char * vmsname = strchr(filename + 1, '/') ?
  897.       HTVMS_name(nodename, filename) : filename + 1;
  898.     fp = fopen(vmsname, "r", "shr=put", "shr=upd");
  899.     
  900. /*    If the file wasn't VMS syntax, then perhaps it is ultrix
  901. */
  902.     if (!fp) {
  903.         char ultrixname[INFINITY];
  904.         if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
  905.         sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
  906.           fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
  907.         if (!fp) {
  908.         if (TRACE) fprintf(stderr, 
  909.                    "HTFile: Can't open as %s\n", ultrixname);
  910.         }
  911.     }
  912.         if (fp)
  913.         {
  914.         if (HTEditable(vmsname)) {
  915.         HTAtom * put = HTAtom_for("PUT");
  916.         HTList * methods = HTAnchor_methods(anchor);
  917.         if (HTList_indexOf(methods, put) == (-1)) {
  918.                HTList_addObject(methods, put);
  919.             }
  920.         }
  921.         HTParseFile(format, format_out, anchor, fp, sink);
  922.         fclose(fp);
  923.         FREE(filename);
  924.             return HT_LOADED;
  925.         }  /* If successfull open */
  926.     FREE(filename);
  927.     }
  928.  
  929. #else /* Unix: */
  930.  
  931.     format = HTFileFormat(filename, &encoding);
  932.     FREE(filename);
  933.     
  934. /*    For unix, we try to translate the name into the name of a transparently
  935. **    mounted file.
  936. **
  937. **    Not allowed in secure (HTClienntHost) situations TBL 921019
  938. */
  939. #ifndef NO_UNIX_IO
  940.     /*  Need protection here for telnet server but not httpd server */
  941.      
  942.     if (!HTSecure) {        /* try local file system */
  943.     char * localname = HTLocalName(addr);
  944.     struct stat dir_info;
  945. #ifdef LONG_LIST
  946.     char buf[80], type, *datestr;
  947.     struct stat st;
  948.     struct passwd *p;
  949.     struct group *g;
  950.     time_t now;
  951.     static char *pbits[] = { "---", "--x", "-w-", "-wx", 
  952.         "r--", "r-x", "rw-", "rwx", 0 };
  953.     static char *psbits[] = { "--S", "--s", "-wS", "-ws", 
  954.         "r-S", "r-s", "rwS", "rws", 0 };
  955. #define SEC_PER_YEAR    (60 * 60 * 24 * 365)
  956. #define PBIT(a, n, s)  (s) ? psbits[((a) >> (n)) & 0x7] : \
  957.     pbits[((a) >> (n)) & 0x7]
  958. #endif /* LONG_LIST */
  959.     
  960. #ifdef GOT_READ_DIR
  961.  
  962. /*              Multiformat handling
  963. **
  964. **    If needed, scan directory to find a good file.
  965. **  Bug:  we don't stat the file to find the length
  966. */
  967.     if ( (strlen(localname) > strlen(MULTI_SUFFIX))
  968.        && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
  969.                       MULTI_SUFFIX))) {
  970.         DIR *dp;
  971.  
  972.         STRUCT_DIRENT * dirbuf;
  973.         float best = NO_VALUE_FOUND;    /* So far best is bad */
  974.         HTFormat best_rep = NULL;    /* Set when rep found */
  975.         STRUCT_DIRENT best_dirbuf;    /* Best dir entry so far */
  976.  
  977.         char * base = strrchr(localname, '/');
  978.         int baselen;
  979.  
  980.         if (!base || base == localname) goto forget_multi;
  981.         *base++ = 0;        /* Just got directory name */
  982.         baselen = strlen(base)- strlen(MULTI_SUFFIX);
  983.         base[baselen] = 0;    /* Chop off suffix */
  984.  
  985.         dp = opendir(localname);
  986.         if (!dp) {
  987. forget_multi:
  988.         FREE(localname);
  989.         return HTLoadError(sink, 500,
  990.             "Multiformat: directory scan failed.");
  991.         }
  992.         
  993.         while ((dirbuf = readdir(dp))!=0) {
  994.         /* while there are directory entries to be read */
  995.         if (dirbuf->d_ino == 0)
  996.             continue;    /* if the entry is not being used, skip it */
  997.  
  998.         if (
  999. #if !defined(SVR4) && !defined(ISC) && !defined(SCO)
  1000.                     (int)dirbuf->d_namlen > baselen &&      /* Match? */
  1001. #endif /* !SVR4 && !ISC && !SCO */
  1002.             !strncmp(dirbuf->d_name, base, baselen)) {    
  1003.             HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
  1004.             float value = HTStackValue(rep, format_out,
  1005.                             HTFileValue(dirbuf->d_name),
  1006.                         0.0  /* @@@@@@ */);
  1007.             if (value != NO_VALUE_FOUND) {
  1008.                 if (TRACE) fprintf(stderr,
  1009.                 "HTFile: value of presenting %s is %f\n",
  1010.                 HTAtom_name(rep), value);
  1011.             if  (value > best) {
  1012.                 best_rep = rep;
  1013.                 best = value;
  1014.                 best_dirbuf = *dirbuf;
  1015.                }
  1016.             }    /* if best so far */             
  1017.          } /* if match */  
  1018.             
  1019.         } /* end while directory entries left to read */
  1020.         closedir(dp);
  1021.         
  1022.         if (best_rep) {
  1023.         format = best_rep;
  1024.         base[-1] = '/';        /* Restore directory name */
  1025.         base[0] = 0;
  1026.         StrAllocCat(localname, best_dirbuf.d_name);
  1027.         goto open_file;
  1028.         
  1029.         } else {             /* If not found suitable file */
  1030.         FREE(localname);
  1031.         return HTLoadError(sink, 403,    /* List formats? */
  1032.            "Could not find suitable representation for transmission.");
  1033.         }
  1034.         /*NOTREACHED*/
  1035.     } /* if multi suffix */
  1036. /*
  1037. **    Check to see if the 'localname' is in fact a directory.  If it is
  1038. **    create a new hypertext object containing a list of files and 
  1039. **    subdirectories contained in the directory.  All of these are links
  1040. **      to the directories or files listed.
  1041. **      NB This assumes the existance of a type 'STRUCT_DIRENT', which will
  1042. **      hold the directory entry, and a type 'DIR' which is used to point to
  1043. **      the current directory being read.
  1044. */
  1045.     
  1046.     
  1047.     if (stat(localname,&dir_info) == -1) {     /* get file information */
  1048.                                    /* if can't read file information */
  1049.         if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
  1050.  
  1051.     }  else {        /* Stat was OK */
  1052.         
  1053.  
  1054.         if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
  1055.         /* if localname is a directory */    
  1056.  
  1057.         HTStructured* target;        /* HTML object */
  1058.         HTStructuredClass targetClass;
  1059.  
  1060.         DIR *dp;
  1061.         STRUCT_DIRENT * dirbuf;
  1062.         
  1063.         char * logical=0;
  1064.         char * pathname=0;
  1065.         char * tail=0;
  1066.         
  1067.         BOOL present[HTML_A_ATTRIBUTES];
  1068.         
  1069.         char * tmpfilename = NULL;
  1070.         struct stat file_info;
  1071.         
  1072.         if (TRACE)
  1073.             fprintf(stderr,"%s is a directory\n",localname);
  1074.             
  1075. /*    Check directory access.
  1076. **    Selective access means only those directories containing a
  1077. **    marker file can be browsed
  1078. */
  1079.         if (HTDirAccess == HT_DIR_FORBID) {
  1080.             FREE(localname);
  1081.             return HTLoadError(sink, 403,
  1082.             "Directory browsing is not allowed.");
  1083.         }
  1084.  
  1085.  
  1086.         if (HTDirAccess == HT_DIR_SELECTIVE) {
  1087.             char * enable_file_name = 
  1088.             malloc(strlen(localname)+ 1 +
  1089.              strlen(HT_DIR_ENABLE_FILE) + 1);
  1090.             strcpy(enable_file_name, localname);
  1091.             strcat(enable_file_name, "/");
  1092.             strcat(enable_file_name, HT_DIR_ENABLE_FILE);
  1093.             if (stat(enable_file_name, &file_info) != 0) {
  1094.             FREE(localname);
  1095.             return HTLoadError(sink, 403,
  1096.             "Selective access is not enabled for this directory");
  1097.             }
  1098.         }
  1099.  
  1100.  
  1101.         dp = opendir(localname);
  1102.         if (!dp) {
  1103.             FREE(localname);
  1104.             return HTLoadError(sink, 403, "This directory is not readable.");
  1105.         }
  1106.  
  1107.  
  1108.  /*    Directory access is allowed and possible
  1109.  */
  1110.         logical = HTAnchor_address((HTAnchor*)anchor);
  1111.             pathname = HTParse(logical, "", 
  1112.                     PARSE_PATH + PARSE_PUNCTUATION);
  1113.  
  1114.             if(!strcmp(pathname,"/"))  /* root path */
  1115.                 StrAllocCopy (tail, "/foo/..");
  1116.                 else
  1117.               {
  1118.                 char * p = strrchr(pathname, '/');  /* find lastslash */
  1119.                 StrAllocCopy(tail, p+1); /* take slash off the beginning */
  1120.               }
  1121.             FREE(pathname);
  1122.         
  1123.         target = HTML_new(anchor, format_out, sink);
  1124.         targetClass = *target->isa;    /* Copy routine entry points */
  1125.             
  1126.           { int i;
  1127.             for(i=0; i<HTML_A_ATTRIBUTES; i++)
  1128.                 present[i] = (i==HTML_A_HREF);
  1129.         }
  1130.         
  1131.                 HTDirTitles(target, (HTAnchor *)anchor);
  1132.  
  1133. #ifdef DIRED_SUPPORT
  1134.         HTAnchor_setFormat((HTParentAnchor *) anchor, WWW_DIRED);
  1135.         lynx_edit_mode = TRUE;
  1136. #endif /* DIRED_SUPPORT */
  1137.                 if (HTDirReadme == HT_DIR_README_TOP)
  1138.             do_readme(target, localname);
  1139.         {
  1140.             HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
  1141.  
  1142.             while ((dirbuf = readdir(dp))!=0)
  1143.             {
  1144.             /* while there are directory entries to be read */
  1145.                 HTBTElement * dirname = NULL;
  1146.  
  1147.                 if (dirbuf->d_ino == 0)
  1148.                 /* if the entry is not being used, skip it */
  1149.                 continue;
  1150.  
  1151.             if ((*(dirbuf->d_name)=='.') ||
  1152.                 (*(dirbuf->d_name)==','))
  1153.                 /* skip those files whose name begins
  1154.                  * with '.' or ',' */
  1155.                 continue;
  1156.  
  1157.             dirname = (HTBTElement *)malloc(
  1158.                     strlen(dirbuf->d_name) + 4);
  1159.             if (dirname == NULL) outofmem(__FILE__,"DirRead");
  1160.             StrAllocCopy(tmpfilename,localname);
  1161.             if (strcmp(localname,"/")) 
  1162.                 /* if filename is not root directory */
  1163.                 StrAllocCat(tmpfilename,"/"); 
  1164.  
  1165.             StrAllocCat(tmpfilename,dirbuf->d_name);
  1166.             stat(tmpfilename, &file_info);
  1167.             if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
  1168. #ifndef DIRED_SUPPORT
  1169.                     sprintf((char *)dirname,"D%s",dirbuf->d_name);
  1170.             else
  1171.                 sprintf((char *)dirname,"F%s",dirbuf->d_name);
  1172.                 /* D & F to have first directories, then files */
  1173. #else
  1174.                 if (dir_list_style == MIXED_STYLE)
  1175.                     sprintf((char *)dirname," %s/",dirbuf->d_name);
  1176.                 else
  1177.                     sprintf((char *)dirname,"D%s",dirbuf->d_name);
  1178.             else if (dir_list_style == MIXED_STYLE)
  1179.                 sprintf((char *)dirname," %s",dirbuf->d_name);
  1180.             else if (dir_list_style == FILES_FIRST)
  1181.                 sprintf((char *)dirname,"C%s",dirbuf->d_name);
  1182.                 /* C & D to have first files, then directories */
  1183.             else
  1184.                 sprintf((char *)dirname,"F%s",dirbuf->d_name);
  1185. #endif /* !DIRED_SUPPORT */
  1186.             HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
  1187.             }
  1188.  
  1189.             /*    Run through tree printing out in order
  1190.              */
  1191.             {
  1192.                 HTBTElement * next_element = HTBTree_next(bt,NULL);
  1193.                 /* pick up the first element of the list */
  1194.             char state;
  1195.                 /* I for initial (.. file),
  1196.                    D for directory file,
  1197.                    F for file */
  1198.             
  1199. #ifdef DIRED_SUPPORT
  1200.             char test;
  1201. #endif /* DIRED_SUPPORT */
  1202.             state = 'I';
  1203.  
  1204.             while (next_element != NULL)
  1205.                 {
  1206.                 char *entry, *file_extra;
  1207.  
  1208.                 StrAllocCopy(tmpfilename,localname);
  1209.                 if (strcmp(localname,"/")) 
  1210.  
  1211.                     /* if filename is not root directory */
  1212.                     StrAllocCat(tmpfilename,"/"); 
  1213.  
  1214.                 StrAllocCat(tmpfilename,
  1215.                     (char *)HTBTree_object(next_element)+1);
  1216.                 /* append the current entry's filename to the path */
  1217.                 HTSimplify(tmpfilename);
  1218.                 /* Output the directory entry */
  1219.                 if (strcmp((char *)
  1220.                          (HTBTree_object(next_element)),"D.."))
  1221.                 {                
  1222. #ifdef DIRED_SUPPORT
  1223.                     test = *(char *) (HTBTree_object(next_element))=='D'?'D':'F';
  1224.                 if (state != test)
  1225.                 {
  1226.                     if (dir_list_style == FILES_FIRST) {
  1227.                        if (state == 'F')
  1228.                         END(HTML_DIR); 
  1229.                     } else if (dir_list_style != MIXED_STYLE)
  1230.                        if (state == 'D')
  1231.                         END(HTML_DIR); 
  1232.                     state = *(char *)
  1233.                         (HTBTree_object(next_element))=='D'?'D':'F';
  1234.                     START(HTML_H2);
  1235.                     if (dir_list_style != MIXED_STYLE)
  1236.                        PUTS(state == 'D'?"Directories:":"Files");
  1237. #else
  1238.                 if (state != *(char *)(HTBTree_object(next_element))) 
  1239.                 {
  1240.                     if (state == 'D')
  1241.                         END(HTML_DIR);
  1242.                     state = *(char *)
  1243.                         (HTBTree_object(next_element))=='D'?'D':'F';
  1244.                     START(HTML_H2);
  1245.                     PUTS(state == 'D'?"Subdirectories:":"Files");
  1246. #endif /* DIRED_SUPPORT */
  1247.                     END(HTML_H2);
  1248.                     START(HTML_DIR);
  1249.                 }
  1250.                     START(HTML_LI);
  1251.                 }
  1252.                 entry = (char*)HTBTree_object(next_element)+1;
  1253.                 file_extra = NULL;
  1254. #ifdef LONG_LIST
  1255.                 START(HTML_PRE);
  1256.                 if(lstat(tmpfilename, &st) != -1) {
  1257.                   char link_name[1025];
  1258.                   int file_len;
  1259.  
  1260.                   /* mode (permissions) and link count */
  1261.                   switch(st.st_mode & S_IFMT) {
  1262.                   case S_IFIFO: type = 'p'; break;
  1263.                   case S_IFCHR: type = 'c'; break;
  1264.                   case S_IFDIR: type = 'd'; break;
  1265.                   case S_IFBLK: type = 'b'; break;
  1266.                   case S_IFREG: type = '-'; break;
  1267.                   case S_IFLNK: 
  1268.                 type = 'l'; 
  1269.                 if ((file_len = readlink(tmpfilename,
  1270.                              link_name,
  1271.                              sizeof(link_name) - 1)
  1272.                      ) >=0) {
  1273.                     StrAllocCopy(file_extra, " -> ");
  1274.                     link_name[file_len] = '\0';
  1275.                     StrAllocCat(file_extra, link_name);
  1276.                 }
  1277.                 break;
  1278.                   case S_IFSOCK: type = 's'; break;
  1279.                   default: type = '?'; break;
  1280.                   }
  1281.                   sprintf(buf, "    %c%s%s%s  %3d ", type,
  1282.                     PBIT(st.st_mode, 6, st.st_mode & S_ISUID),
  1283.                     PBIT(st.st_mode, 3, st.st_mode & S_ISGID), 
  1284.                     PBIT(st.st_mode, 0, 0),
  1285.                 st.st_nlink);
  1286.                   PUTS(buf);
  1287.  
  1288.                   /* user */
  1289.                   p = getpwuid(st.st_uid);
  1290.                   if(p) 
  1291.                 sprintf(buf, "%-8.8s ", p->pw_name);
  1292.                   else
  1293.                 sprintf(buf, "%-8d ", st.st_uid);
  1294.                   PUTS(buf);
  1295.  
  1296.                   /* group */
  1297.                   g = getgrgid(st.st_gid);
  1298.                   if(g)
  1299.                 sprintf(buf, "%-8.8s ", g->gr_name);
  1300.                   else
  1301.                 sprintf(buf, "%-8d ", st.st_gid);
  1302.                   PUTS(buf);
  1303.  
  1304.                   /* size */
  1305.                   sprintf(buf, "%7d ", st.st_size);
  1306.                   PUTS(buf);
  1307.  
  1308.                   /* date */
  1309.                   now = time(0);
  1310.                   datestr = ctime(&st.st_mtime);
  1311.                   if((now - st.st_mtime) < SEC_PER_YEAR/2) 
  1312.                 /* MMM DD HH:MM */
  1313.                 sprintf(buf, "%.12s ", datestr + 4);
  1314.                   else 
  1315.                 /* MMM DD  YYYY */
  1316.                 sprintf(buf, "%.7s %.4s ", datestr + 4, 
  1317.                   datestr + 20);
  1318.                   PUTS(buf);
  1319.                 }
  1320. #endif /* LONG_LIST */
  1321.                 HTDirEntry(target, tail, entry);
  1322.                 PUTS(entry);
  1323.                     END(HTML_A);
  1324.                 if (file_extra) {
  1325.                 PUTS(file_extra);
  1326.                 FREE(file_extra);
  1327.                 }
  1328. #ifdef LONG_LIST
  1329.                   END(HTML_PRE);
  1330. #endif /* LONG_LIST */
  1331.  
  1332.                 next_element = HTBTree_next(bt,next_element);
  1333.                     /* pick up the next element of the list; 
  1334.                  if none, return NULL*/
  1335.             }
  1336.             if (state == 'I')
  1337.             {
  1338.                 START(HTML_P);
  1339.                 PUTS("Empty Directory");
  1340.             }
  1341.             else
  1342.                 END(HTML_DIR);
  1343.             }
  1344.  
  1345.                 /* end while directory entries left to read */
  1346.             closedir(dp);
  1347.             FREE(logical);
  1348.             FREE(tmpfilename);
  1349.             FREE(tail);
  1350.             HTBTreeAndObject_free(bt);
  1351.  
  1352.             if (HTDirReadme == HT_DIR_README_BOTTOM)
  1353.               do_readme(target, localname);
  1354.             FREE_TARGET;
  1355.             FREE(localname);
  1356.             return HT_LOADED;    /* document loaded */
  1357.         }
  1358.  
  1359.         } /* end if localname is directory */
  1360.     
  1361.     } /* end if file stat worked */
  1362.     
  1363. /* End of directory reading section
  1364. */
  1365. #endif /* GOT_READ_DIR */
  1366. open_file:
  1367.     {
  1368.         FILE * fp = fopen(localname,"r");
  1369.  
  1370.         if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
  1371.                 localname, (void*)fp);
  1372.         if (fp) {        /* Good! */
  1373.         if (HTEditable(localname)) {
  1374.             HTAtom * put = HTAtom_for("PUT");
  1375.             HTList * methods = HTAnchor_methods(anchor);
  1376.             if (HTList_indexOf(methods, put) == (-1)) {
  1377.             HTList_addObject(methods, put);
  1378.             }
  1379.         }
  1380.         FREE(localname);
  1381.         HTParseFile(format, format_out, anchor, fp, sink);
  1382.         fclose(fp);
  1383.         return HT_LOADED;
  1384.         }  /* If succesfull open */
  1385.     }    /* scope of fp */
  1386.     }  /* local unix file system */    
  1387. #endif /* !NO_UNIX_IO */
  1388. #endif /* VMS */
  1389.  
  1390. #ifndef DECNET
  1391. /*    Now, as transparently mounted access has failed, we try FTP.
  1392. */
  1393.     {
  1394.     /** Deal with case-sensitivity differences on VMS verus Unix **/
  1395. #ifdef VMS
  1396.         if (strcasecomp(nodename, HTHostName())!=0)
  1397. #else
  1398.     if (strcmp(nodename, HTHostName())!=0)
  1399. #endif /* VMS */
  1400.         if(!strncmp(addr,"file://localhost",16))
  1401.             return -1;  /* never go to ftp site when URL
  1402.                  * is file://localhost
  1403.                  */
  1404.         else
  1405.             return HTFTPLoad(addr, anchor, format_out, sink);
  1406.     }
  1407. #endif /* !DECNET */
  1408.  
  1409. /*    All attempts have failed.
  1410. */
  1411.     {
  1412.         if (TRACE)
  1413.         fprintf(stderr, "Can't open `%s', errno=%d\n", addr, SOCKET_ERRNO);
  1414.  
  1415.     return HTLoadError(sink, 403, "Can't access requested file.");
  1416.     }
  1417.     
  1418.  
  1419. }
  1420.  
  1421. /*        Protocol descriptors
  1422. */
  1423. GLOBALDEF PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFile, 0 };
  1424. GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };
  1425.